/*
 * @Author: Superficial
 * @Date: 2021-02-25 00:46:04
 * @LastEditTime: 2021-02-25 12:21:59
 * @Description: 音乐服务
 */
import { inject, readonly, reactive, computed } from "vue";

import { getMusicList } from "@/api/global"
import { musicId } from "@/config/app.config";

const createMusicPlayer = (config) => {
  const  { amplitude } = config

  const initVolume = config.volume ?? 40

  const state = reactive({
    // 是否初始化
    inited: false,
    // 是否可用
    ready: false,
    // 活动项
    index: 0,
    // 总数
    count: 0,
    // 音量
    volume: initVolume,
    // 图形化
    wave: false,
    // 播放中
    playing: false,
    // 进度
    speeds: 0,
    progress: 0
  })

  // 静音
  const muted = computed(() => state.volume === 0)

  // 原始播放列表
  const songList = reactive({
    fetching: false,
    data: []
  })

  // 可消费播放列表
  const playableSongList = computed(() => {
    return songList.data.map(song => {
      return {
        ...song,
        url: `https://music.163.com/song/media/outer/url?id=${song.id}.mp3`,
        cover_art_url: song.cover_art_url
      }
    })
  })

  // 获取歌曲列表
  const fetchSongList = async () => {
    try {
      songList.fetching = true
      songList.data = await getMusicList(musicId)
      state.count = songList.length
    } catch(error) {
      songList.data = []
      state.count = 0
      throw error
    } finally {
      songList.fetching = false
    }
  }

  // 获取当前歌曲元数据
  const currentSong = computed(() => {
    if (state.inited && state.index !== undefined) {
      // https://521dimensions.com/open-source/amplitudejs/docs/functions/#get-active-song-metadata
      return amplitude.getActiveMetadata()
    }
  })

  const play = () => amplitude.play()
  const pause = () => amplitude.pause()
  const prevSong = () => amplitude.prev()
  const nextSong = () => amplitude.next()
  const changeVolume = (volume) => amplitude.setVolume(volume)
  const toggleMuted = () => changeVolume(muted.value ? initVolume : 0 )
  const togglePlay = () => amplitude.getPlayerState() === 'playing' ? pause() : play()

  // 初始化播放器
  const initPlayer = (songs) => {
    amplitude.init({
      delay: 668,
      debug: false,
      volume: state.volume,
      songs,
      start_song: 0,
      continue_next: true,
      callbacks: {
        initialized: () => {
          state.ready = true
          state.inited = true
        },
        play: () => {
          state.ready = true
          state.wave = true
          state.playing = true
        },
        play: () => {
          state.wave = false
          state.playing = false
        },
        stop: () => {
          state.wave = false
          state.playing = false
        },
        volumechange: () => {
          state.volume = amplitude.getVolume()
        },
        timeupdate: () => {
          state.speeds = amplitude.getSongPlayedSeconds()
          state.progress = amplitude.getSongPlayedPercentage()
        },
        song_change: () => {
          state.index = amplitude.getActiveIndex()
        },
        ended: () => {
          state.playing = false
        },
        error: (error) => {
          console.warn('播放器出现异常，自动下一首！', state.index, error)
          state.playing = false
          // 播放异常时不再清除音乐，不作 url 可能不可用的假设
          // amplitude.removeSong(state.index)
          // HACK: 网络阻塞会导致紧邻的后续请求中断，所以下一首操作需要延时，避免瀑布式请求
          window.setTimeout(nextSong, 1668)
        }
      }
    })
    amplitude.setRepeat(true)
  }

  const start = () => {
    fetchSongList().then(() => {
      if (!playableSongList.value.length) {
        state.ready = true
        console.warn("播放列表为空，未找到有效音乐，无法初始化！")
        return
      }

      initPlayer(playableSongList.value)
    }).catch(error => {
      state.ready = false
      console.log("播放列表请求失败，无法初始化", error)
    })
  }

  return {
    state: readonly(state),
    muted,
    currentSong,

    start,
    play,
    pause,
    changeVolume,
    toggleMuted,
    togglePlay,
    prevSong,
    nextSong
  }
}

export const createMusic = (config) => {
  const musicPlayer = createMusicPlayer(config)
  if (config.autoStart) {
    musicPlayer.start()
  }

  return {
    ...musicPlayer,
    install(app) {
      app.provide("music", musicPlayer)
    }
  }
}

export const useMusic = () => {
  return inject("music")
}